Package net.mitza.rel.parser

Source Code of net.mitza.rel.parser.Parser

/**
* This parser is based on the CogPar implementation with additional logical expression parsing features.
*
* CogPar is lightweight but versatile parser for mathematical expressions.
*
* It can be used to analyse expressions and store them in an internal data structure for later
* evaluation. Repeated evaluation of the same expression using CogPar is fast.
*
* CogPar comes with a highly configurable tokenizer which can be adapted for your own needs.
*
* Arbitrary named variables are supported and values can be assigned in a single line of code.
*
* The parser, it's grammar an the tokenizer are well documented. You can read more about the internal
* workings of CogPar <a href="http://cogitolearning.co.uk/?p=523" alt="CogPar tutorial">in these posts</a>.
*
* CogPar is distributed under the MIT license, so feel free to use it in your own projects.
*
* To download CogPar, <a href="" alt="Download CogPar">follow this link.</a>
*/
package net.mitza.rel.parser;

import java.util.LinkedList;

import net.mitza.rel.parser.expression.AdditionExpressionNode;
import net.mitza.rel.parser.expression.AndExpressionNode;
import net.mitza.rel.parser.expression.ComparatorExpressionNode;
import net.mitza.rel.parser.expression.ConstantExpressionNode;
import net.mitza.rel.parser.expression.ExponentiationExpressionNode;
import net.mitza.rel.parser.expression.ExpressionNode;
import net.mitza.rel.parser.expression.FunctionExpressionNode;
import net.mitza.rel.parser.expression.MultiplicationExpressionNode;
import net.mitza.rel.parser.expression.NotExpressionNode;
import net.mitza.rel.parser.expression.OrExpressionNode;
import net.mitza.rel.parser.expression.VariableExpressionNode;

import com.tridion.tcdl.TransformContext;

/**
* A parser for mathematical and logical expressions. The parser class defines a method parseExpression() which takes a
* string and returns an ExpressionNode that holds a representation of the mathematical expression. The method
* parseCondition() parses a logical expression to a 'true' or 'false' value.
*
* Parsing is implemented in the form of a recursive descent parser.
*/
public class Parser {

  // logical -> or_term or_op
  // or_op -> OR or_term or_op
  // or_op -> EPSILON
  //
  // or_term -> and_term and_op
  // and_op -> AND and_term and_op
  // and_op -> EPSILON
  //
  // and_term -> not_term
  // and_term -> NOT not_term
  //
  // not_term -> OPEN_PARENTHESIS logical CLOSE_PARENTHESIS
  // not_term -> proposition
  //
  // proposition -> expression comp_op
  // comp_op -> COMPARATOR expression
  // comp_op -> EPSILON
  //
  //
  // expression -> signed_term sum_op
  // sum_op -> PLUSMINUS term sum_op
  // sum_op -> EPSILON
  //
  // signed_term -> PLUSMINUS term
  // signed_term -> term
  //
  // term -> factor prod_op
  // prod_op -> MULTDIV factor prod_op
  // prod_op -> EPSILON
  //
  // factor -> FUNCTION factor
  // factor -> argument raise_op
  // raise_op -> RAISED argument
  // raise_op -> EPSILON
  //
  // argument -> value
  // argument -> OPEN_PARENTHESIS expression CLOSE_PARENTHESIS
  //
  // value -> NUMBER
  // value -> variable
  //
  // variable -> bean bean_op
  // bean_op -> DOT property bean_op
  // bean_op -> OPEN_BRACKET expression CLOSE_BRACKET bean_op
  // bean_op -> EPSILON

  private final boolean USE_CACHE = true; // performance gain with caching x3.6

  /** the tokens to parse */
  LinkedList<Token> tokens;
  /** the next token */
  Token lookahead;
  private TransformContext context;
  private ExpressionCache cache;

  public Parser() {
    if (USE_CACHE) {
      cache = ExpressionCache.getInstance();
    }
  }

  public Parser(TransformContext context) {
    this();
    this.context = context;
  }

  public ExpressionNode parseCondition(String expression) {
    ExpressionNode logicalExpression = null;
    if (USE_CACHE) {
      logicalExpression = cache.get(expression);
    }

    if (logicalExpression == null) {
      Tokenizer tokenizer = Tokenizer.getExpressionTokenizer();
      tokenizer.tokenize(expression);
      LinkedList<Token> tokens = tokenizer.getTokens();
      logicalExpression = parseCondition(tokens);
      if (USE_CACHE) {
        cache.put(expression, logicalExpression);
      }
    } else {
      logicalExpression.accept(new SetContextVisitor(context));
    }

    return logicalExpression;
  }

  public ExpressionNode parseCondition(LinkedList<Token> tokens) {
    this.tokens = new LinkedList<Token>(tokens);
    lookahead = this.tokens.getFirst();

    ExpressionNode logicalExpression = logicalExpression();
    logicalExpression.accept(new SetContextVisitor(context));

    return logicalExpression;
  }

  /**
   * Parse a mathematical expression in a string and return an ExpressionNode.
   *
   * This is a convenience method that first converts the string into a linked list of tokens using the expression
   * tokenizer provided by the Tokenizer class.
   *
   * @param expression
   *            the string holding the input
   * @return the internal representation of the expression in form of an expression tree made out of ExpressionNode
   *         objects
   */
  public ExpressionNode parseExpression(String expression) {
    ExpressionNode mathematicalExpression = null;
    if (USE_CACHE) {
      mathematicalExpression = cache.get(expression);
    }

    if (mathematicalExpression == null) {
      Tokenizer tokenizer = Tokenizer.getExpressionTokenizer();
      tokenizer.tokenize(expression);
      LinkedList<Token> tokens = tokenizer.getTokens();
      mathematicalExpression = parseExpression(tokens);
      if (USE_CACHE) {
        cache.put(expression, mathematicalExpression);
      }
    } else {
      mathematicalExpression.accept(new SetContextVisitor(context));
    }

    return mathematicalExpression;
  }

  /**
   * Parse a mathematical expression in contained in a list of tokens and return an ExpressionNode.
   *
   * @param tokens
   *            a list of tokens holding the tokenized input
   * @return the internal representation of the expression in form of an expression tree made out of ExpressionNode
   *         objects
   */
  public ExpressionNode parseExpression(LinkedList<Token> tokens) {
    this.tokens = new LinkedList<Token>(tokens);
    lookahead = this.tokens.getFirst();

    ExpressionNode mathematicalExpression = mathematicalExpression();
    mathematicalExpression.accept(new SetContextVisitor(context));

    return mathematicalExpression;
  }

  // logical -> or_term or_op
  private ExpressionNode logicalExpression() {
    // only one rule
    // logical -> or_term or_op
    ExpressionNode expression = orTerm();
    expression = orOperation(expression);
    return expression;
  }

  // or_op -> OR or_term or_op
  // or_op -> EPSILON
  private ExpressionNode orOperation(ExpressionNode expression) {
    // or_op -> OR or_term or_op
    if (lookahead.tokenType == TokenTypes.OR) {
      OrExpressionNode disjunction;
      // This means we are actually dealing with an OR
      // If expr is not already an OR, we have to create one
      if (expression.getType() == TokenTypes.OR) {
        disjunction = (OrExpressionNode) expression;
      } else {
        disjunction = new OrExpressionNode(expression);
      }

      nextToken();
      ExpressionNode term = orTerm();
      disjunction.add(term);

      return orOperation(disjunction);
    }

    // or_op -> EPSILON
    return expression;
  }

  // or_term -> and_term and_op
  private ExpressionNode orTerm() {
    // or_term -> and_term and_op
    ExpressionNode term = andTerm();
    return andOperation(term);
  }

  // and_op -> AND and_term and_op
  // and_op -> EPSILON
  private ExpressionNode andOperation(ExpressionNode expression) {
    // and_op -> AND and_term and_op
    if (lookahead.tokenType == TokenTypes.AND) {
      AndExpressionNode conjunction;
      // This means we are actually dealing with an AND
      // If expr is not already an AND, we have to create one
      if (expression.getType() == TokenTypes.AND) {
        conjunction = (AndExpressionNode) expression;
      } else {
        conjunction = new AndExpressionNode(expression);
      }

      nextToken();
      ExpressionNode term = andTerm();
      conjunction.add(term);

      return andOperation(conjunction);
    }

    // and_op -> EPSILON
    return expression;
  }

  // and_term -> not_term
  // and_term -> NOT not_term
  private ExpressionNode andTerm() {
    // and_term -> NOT not_term
    if (lookahead.tokenType == TokenTypes.NOT) {
      nextToken();
      ExpressionNode term = notTerm();
      return new NotExpressionNode(term);
    }

    // and_term -> not_term
    return notTerm();
  }

  // not_term -> OPEN_PARENTHESIS logical CLOSE_PARENTHESIS
  // not_term -> proposition
  private ExpressionNode notTerm() {
    // not_term -> OPEN_PARENTHESIS logical CLOSE_PARENTHESIS
    if (lookahead.tokenType == TokenTypes.OPEN_PARENTHESIS) {
      nextToken();
      ExpressionNode logicalExpression = logicalExpression();
      if (lookahead.tokenType != TokenTypes.CLOSE_PARENTHESIS) {
        throw new ParserException("Closing parenthesis expected", lookahead);
      }
      nextToken();
      return logicalExpression;
    }

    // not_term -> proposition
    return logicalProposition();
  }

  // proposition -> expression comp_op
  private ExpressionNode logicalProposition() {
    // proposition -> expression comp_op
    ExpressionNode expression = mathematicalExpression();
    expression = comparisonOperation(expression);
    return expression;
  }

  // comp_op -> COMPARATOR expression
  // comp_op -> EPSILON
  private ExpressionNode comparisonOperation(ExpressionNode leftHand) {
    // comp_op -> COMPARATOR expression comp_op
    if (lookahead.tokenType == TokenTypes.COMPARATOR) {
      String operator = lookahead.sequence;
      nextToken();
      ExpressionNode rightHand = mathematicalExpression();
      return new ComparatorExpressionNode(leftHand, rightHand, operator);
    }

    // comp_op -> EPSILON
    return leftHand;
  }

  // expression -> signed_term sum_op
  private ExpressionNode mathematicalExpression() {
    // only one rule
    // expression -> signed_term sum_op
    ExpressionNode signedTerm = signedTerm();
    signedTerm = sumOperation(signedTerm);
    return signedTerm;
  }

  // sum_op -> PLUSMINUS term sum_op
  // sum_op -> EPSILON
  private ExpressionNode sumOperation(ExpressionNode expression) {
    // sum_op -> PLUSMINUS term sum_op
    if (lookahead.tokenType == TokenTypes.ADDITION) {
      AdditionExpressionNode sum;
      // This means we are actually dealing with a sum
      // If expr is not already a sum, we have to create one
      if (expression.getType() == TokenTypes.ADDITION) {
        sum = (AdditionExpressionNode) expression;
      } else {
        sum = new AdditionExpressionNode(expression, true);
      }

      // reduce the input and recursively call sum_op
      boolean positive = lookahead.sequence.equals("+");
      nextToken();
      ExpressionNode term = term();
      sum.add(term, positive);

      return sumOperation(sum);
    }

    // sum_op -> EPSILON
    return expression;
  }

  // signed_term -> PLUSMINUS term
  // signed_term -> term
  private ExpressionNode signedTerm() {
    // signed_term -> PLUSMINUS term
    if (lookahead.tokenType == TokenTypes.ADDITION) {
      boolean positive = lookahead.sequence.equals("+");
      nextToken();
      ExpressionNode term = term();
      if (positive) {
        return term;
      } else {
        return new AdditionExpressionNode(term, false);
      }
    }

    // signed_term -> term
    return term();
  }

  // term -> factor prod_op
  private ExpressionNode term() {
    // term -> factor prod_op
    ExpressionNode factor = factor();
    return prodOperation(factor);
  }

  // prod_op -> MULTDIV factor prod_op
  // prod_op -> EPSILON
  private ExpressionNode prodOperation(ExpressionNode expression) {
    // prod_op -> MULTDIV factor prod_op
    if (lookahead.tokenType == TokenTypes.MULTIPLICATION) {
      MultiplicationExpressionNode prod;
      // This means we are actually dealing with a product
      // If expr is not already a product, we have to create one
      if (expression.getType() == TokenTypes.MULTIPLICATION) {
        prod = (MultiplicationExpressionNode) expression;
      } else {
        prod = new MultiplicationExpressionNode(expression, true);
      }

      // reduce the input and recursively call prod_op
      boolean positive = lookahead.sequence.equals("*");
      nextToken();
      ExpressionNode factor = factor();
      prod.add(factor, positive);

      return prodOperation(prod);
    }

    // prod_op -> EPSILON
    return expression;
  }

  // factor -> FUNCTION factor
  // factor -> argument raise_op
  private ExpressionNode factor() {
    // factor -> FUNCTION argument
    if (lookahead.tokenType == TokenTypes.FUNCTION) {
      int function = FunctionExpressionNode.stringToFunction(lookahead.sequence);
      if (function < 0) {
        throw new ParserException("Unexpected Function '%s' found", lookahead);
      }
      nextToken();
      ExpressionNode factor = factor();
      return new FunctionExpressionNode(function, factor);
    }

    // factor -> argument raise_op
    ExpressionNode argument = argument();
    return raiseOperation(argument);
  }

  // raise_op -> RAISED argument
  // raise_op -> EPSILON
  private ExpressionNode raiseOperation(ExpressionNode expression) {
    // raise_op -> RAISED argument
    if (lookahead.tokenType == TokenTypes.EXPONENTIATION) {
      nextToken();
      ExpressionNode exponent = argument();

      return new ExponentiationExpressionNode(expression, exponent);
    }

    // raise_op -> EPSILON
    return expression;
  }

  // argument -> value
  // argument -> OPEN_PARENTHESIS expression CLOSE_PARENTHESIS
  private ExpressionNode argument() {
    // argument -> OPEN_PARENTHESIS expression CLOSE_PARENTHESIS
    if (lookahead.tokenType == TokenTypes.OPEN_PARENTHESIS) {
      nextToken();
      ExpressionNode expression = mathematicalExpression();
      if (lookahead.tokenType != TokenTypes.CLOSE_PARENTHESIS) {
        throw new ParserException("Closing parentehesis expected", lookahead);
      }
      nextToken();
      return expression;
    }

    // argument -> value
    return value();
  }

  // value -> NUMBER
  // value -> VARIABLE
  private ExpressionNode value() {
    // value -> NUMBER
    if (lookahead.tokenType == TokenTypes.VALUE) {
      ExpressionNode constant = new ConstantExpressionNode(lookahead.sequence);
      nextToken();
      return constant;
    }

    // value -> VARIABLE
    if (lookahead.tokenType == TokenTypes.VARIABLE) {
      ExpressionNode variable = variable();
      return variable;
    }

    if (lookahead.tokenType == TokenTypes.EPSILON) {
      throw new ParserException("Unexpected end of input");
    } else {
      throw new ParserException("Unexpected symbol '%s' found", lookahead);
    }
  }

  // variable -> bean bean_op
  private ExpressionNode variable() {
    VariableExpressionNode bean = new VariableExpressionNode(lookahead.sequence);
    nextToken();
    beanOperation(bean);
    return bean;
  }

  // bean_op -> DOT property bean_op
  // bean_op -> OPEN_BRACKET variable CLOSE_BRACKET bean_op
  // bean_op -> EPSILON
  private void beanOperation(VariableExpressionNode bean) {
    // bean_op -> DOT property bean_op
    if (lookahead.tokenType == TokenTypes.DOT) {
      nextToken();
      VariableExpressionNode property = new VariableExpressionNode(lookahead.sequence);
      bean.setProperty(property);
      nextToken();
      beanOperation(property);
      return;
    }

    // bean_op -> OPEN_BRACKET expression CLOSE_BRACKET bean_op
    if (lookahead.tokenType == TokenTypes.OPEN_BRACKET) {
      nextToken();
      ExpressionNode index = mathematicalExpression();
      bean.setIndex(index);
      if (lookahead.tokenType != TokenTypes.CLOSE_BRACKET) {
        throw new ParserException("Closing bracket expected", lookahead);
      }
      nextToken();
      beanOperation(bean);
      return;
    }

    // bean_op -> EPSILON
    return;
  }

  /**
   * Remove the first token from the list and store the next token in lookahead
   */
  private void nextToken() {
    tokens.pop();
    // at the end of input we return an epsilon token
    if (tokens.isEmpty()) {
      lookahead = new Token(TokenTypes.EPSILON, "");
    } else {
      lookahead = tokens.getFirst();
    }
  }
}
TOP

Related Classes of net.mitza.rel.parser.Parser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.